package grafica.robots; 


import grafica.*;
import grafica.coleccionables.ColeccionableEntity;
import java.awt.Graphics;
import java.util.Stack;
import logica.Orientacion;

/**
 * Clase RobotEntity: modela el robot del juego gráficamente. 
 * @author Amorosi, Javier (94528)
 * @author Antiman, Nehuen (102440)
 */
public abstract class RobotEntity extends Entity {
	
	protected Stack<ColeccionableEntity> baul;
	protected int orientacionLogica;
	
	private boolean girando=false;
	private boolean moviendo=false;

	private int antesX;
	private int antesY;
	private int giro;
	
	/**
	 * Constructor de la clase RobotEntity
	 * @param _game el juego donde se encuentra la entidad. 
	 * @param ref referencia a la representacion gráfica de la entidad.
	 * @param f posición en filas de la entidad.
	 * @param c posición en columnas de la entidad.
	 */
	public RobotEntity(ControlGraficos _game,String ref,int f,int c) {
		super(_game, ref,f,c);
		orientacionLogica=Orientacion.ARRIBA;
		orientacionGrados=0; 
		baul=new Stack<ColeccionableEntity>();
	}
	
	/**
	 * Dibuja al robot en el gráfico dado. 
	 * @param g gráfico donde se dibujará la entidad. 
	 */
	public void draw(Graphics g) {
		switch (orientacionLogica) {
			case Orientacion.ARRIBA: sprite.drawRotated(g,(int) x,(int) y, 0);
			break;
			case Orientacion.ABAJO: sprite.drawRotated(g,(int) x,(int) y, 180);
			break;
			case Orientacion.DERECHA: sprite.drawRotated(g,(int) x,(int) y, 90);
			break;
			case Orientacion.IZQUIERDA: sprite.drawRotated(g,(int) x,(int) y,270);
			break;
		}
	}
	
	/**
	 * Retorna la orientacion lógica del robot.
	 * @return orientacion lógica del robot.
	 */
	public int getOrientacionLogica(){
		return orientacionLogica;
	}
	
	/**
	 * Retorna la orientacion en grados del robot.
	 * @return orientacion en grados del robot.
	 */
	public double getOrientacionGrados(){
		return orientacionGrados;
	}
	
	private boolean moviUnaCasilla(){
		return !(Math.abs(antesX-getX())<game.getPixelesImagenes() && Math.abs(antesY-getY())<game.getPixelesImagenes());
	}
	
	/**
	 * Indica si el robot esta en movimiento o no.
	 * @return true si el robot se esta moviendo, false caso contrario. 
	 */
	public boolean moviendo(){
		moviendo=moviendo && !moviUnaCasilla();
		return moviendo;
	}
	
	/**
	 * Indica si el robot esta girando o no.
	 * @return true si el robot esta girando, false caso contrario. 
	 */
	public boolean girando(){
		return girando;
	}

	/**
	 * Mueve al robot gráficamente, hace que avanze una casilla, a una determinada velocidad.
	 * @param moveSpeed velocidad de movimiento.
	 */
	public void avanzar(double moveSpeed) {
		antesX = getX();
		antesY = getY();
		moviendo=true;
		setHorizontalMovement(0);
		setVerticalMovement(0);
		switch (orientacionLogica){
			case Orientacion.ARRIBA:{
				setVerticalMovement(-moveSpeed);
				f--;
			}break;
			case Orientacion.DERECHA:{
				setHorizontalMovement(moveSpeed);
				c++;
			}break;
			case Orientacion.ABAJO:{
				setVerticalMovement(moveSpeed);
				f++;
			}break;
			case Orientacion.IZQUIERDA:{
				setHorizontalMovement(-moveSpeed);
				c--;
			}break;
		}
	}
	
	/**
	 * Se le requiere a la entidad que se mueva. 
	 * 
	 * @param delta el tiempo desde el último movimiento. 
	 */
	public void move(long delta) {
		super.move(delta);
	}

	/**
	 * Girar al robot dada la dirección de giro d. Esta dirección de giro se modela según la clase
	 * Orientacion. 
	 * @param d dirección de giro.
	 */
	public void girarRobot(int d) {	
		girando=true;
		giro=d;
	}
	
	/**
	 * Dibuja al robot en el gráfico dado pero rotado, para esto toma como párametros
	 * el gráfico donde se va a dibujar el robot, la velocidad de movimiento y la nueva orientación lógica.
	 * @param g gráfico donde se va a dibujar el robot.
	 * @param moveSpeed velocidad de movimiento
	 * @param nueva nueva orientación lógica.
	 */
	public void rotate(Graphics g,double moveSpeed, int nueva) { 
		int delta=45;
    	switch (giro){
		case Orientacion.IZQUIERDA:
			if (((int)orientacionGrados)==0 && orientacionLogica==Orientacion.ARRIBA) 
				orientacionGrados=360;
			if (orientacionGrados>nueva*90){
				orientacionGrados -= delta * moveSpeed/1000;						
				sprite.drawRotated(g,(int) x, (int) y, orientacionGrados);					
			}				
			else
				termineGirar(nueva);
			break;
		case Orientacion.DERECHA:			
			if (orientacionGrados<=nueva*90 || (orientacionLogica==Orientacion.IZQUIERDA && (orientacionGrados)<=360) ){
				orientacionGrados += delta * moveSpeed/1000;						
				sprite.drawRotated(g,(int) x, (int) y, orientacionGrados);					
			}	
			else
				termineGirar(nueva);
    	}
	}
	
	private void termineGirar(int nueva){
		orientacionLogica=nueva;
		orientacionGrados = orientacionLogica*90;
		girando=false;
	}
	
	/**
	 * No se modela algun comportamiento para el robot en caso de chocar con algo.
	 * Operación sin efecto. 
	 * 
	 * @param other la entidad con la que habría chocado el robot.
	 */
	public void collidedWith(Entity other) {

	}

	/**
	 * Accion especial del robot, el efecto de esta operácion es dependiente de las clases que 
	 * extiendan a Robot. 
	 */
	public abstract void accionEspecial();
	
	/**
	 * Guarda algun objecto coleccionable gráfico en el baúl del robot, esto es para mantener
	 * consistencia con lo ocurrido en la lógica del juego.
	 * @param c coleccionable a colocar en el baúl.
	 */
	public void guardarBaul(ColeccionableEntity c){
		baul.push(c);
	}
	
	/**
	 * Quita y retorna el último elemento colocado en el baúl. 
	 * @return último coleccionable colocado en el baúl.  
	 */
	public ColeccionableEntity quitarBaul(){
		return baul.pop();
	}
}